﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;

using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;

using Awesomium.Windows.Forms;

using System.IO;
using System.Security.Permissions;
using Awesomium.Core;
using Winz.JsonModel;


namespace Winz.FoeBot
{

    public partial class Form1 : Form, IResourceInterceptor
    {
        private LogForm logForm;
        private SettingsForm settingsForm;

        private Bot bot;
        Dictionary<MotivateType, Entity> batiments;
        private Dictionary<MotivationOrdersType,Button> motivationBtns;
        private OtherPlayeCity currentPlayerCity;

        private DateTime _timeoutStart;

        private void CurrentDomain_UnhandledException(object o, UnhandledExceptionEventArgs e)
        {
            MessageBox.Show( String.Format("Uncaught Exception : {0} " + Environment.NewLine + "From : {1}", e.ExceptionObject.GetType().ToString(), o.GetType().ToString()),"General Error",MessageBoxButtons.OK,MessageBoxIcon.Error); 
        }

        
        
        public void Log(string message,params object[] args)
        {

            logForm.txtBox.InvokeIfRequired(
                    t => { t.Text = String.Format(message, args) + System.Environment.NewLine + t.Text; }
                    );
        }

        public void SetBtn(Button btn, bool enabled,string text = null) {
            btn.InvokeIfRequired(b =>
            {
                b.Enabled = enabled;
                if(text != null)
                    b.Text = text;
            });
        }

        


        public Form1()
        {
            this.components = new System.ComponentModel.Container();
            InitializeComponent();
            this.components.Add(new Disposer(this.OnDispose));

            Assembly assembly = Assembly.GetExecutingAssembly();
            FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo(assembly.Location);
            string version = fileVersionInfo.ProductVersion;
            string product = fileVersionInfo.ProductName;


            this.Text = product + " v" + version;

            webSessionProvider.DataPath = String.Format("{0}{1}{2}{1}Cache", Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), Path.DirectorySeparatorChar,"FoE");

            this.logForm = new LogForm();

//            this.bot = new Bot(this.Text, "[CLASS:Chrome_RenderWidgetHostHWND; INSTANCE:1]", new Bot.LogHandler(Log));
            bot = new Bot(this.Text, "[CLASS:Internet Explorer_Server; INSTANCE:1]",new Bot.LogHandler(Log));
            bot.webBrowser = webBrowser;
            
            splitContainer1.Panel2Collapsed = true;

            batiments = new Dictionary<MotivateType, Entity>();
            motivationBtns = new Dictionary<MotivationOrdersType, Button>();
            motivationBtns.Add(MotivationOrdersType.PO_MM_MO, button1);
            motivationBtns.Add(MotivationOrdersType.PO_MO_MM, button2);
            motivationBtns.Add(MotivationOrdersType.MM_PO_MO, button3);
            motivationBtns.Add(MotivationOrdersType.MM_MO_PO, button4);
            motivationBtns.Add(MotivationOrdersType.MO_MM_PO, button5);
            motivationBtns.Add(MotivationOrdersType.MO_PO_MM, button6);


            motivationBtns.ToList().ForEach(vp =>
            {
                vp.Value.Text = vp.Key.ToString().Replace("_", "-");
            });



            #region Attach events
            bot.jsonParser.PlayerVisited += new PlayerVisitedEventHandler(OnPlayerVisited);
            bot.jsonParser.PlayerMotivated += new PlayerMotivatedEventHandler(OnPlayerMotivated);
            bot.jsonParser.GameStarted += new GameStartedEventHandler(OnGameStarted);
            bot.socialBarMotivator.MotivatorStarted += new EventHandler(OnBotStarted);
            bot.socialBarMotivator.MotivatorStopped += new EventHandler(OnBotStopped);
            bot.socialBarMotivator.MotivatorPaused += new EventHandler(OnBotPaused);
            bot.socialBarMotivator.PlayerNeedMotivationOrder += new PlayerNeedMotivationOrderEventHandler(OnPlayerNeedMotivationOrder);
            #endregion

        }




        private void OnDispose(bool disposing)
        {
            if (this.bot != null)
                this.bot.Dispose();
        }


        #region Bot Event Handlers
        private void OnGameStarted(object o, MessageEventArgs<StartUp> e)
        {
            SetBtn(btnStart, true, "Start");
            
        }

        private void OnBotStarted(object o, EventArgs e)
        {
            webBrowser.SetBotMode(true);
            SetBtn(btnStart, true, "Stop");
            SetBtn(btnPause, true, "Pause");
        }

        private void OnBotStopped(object o, EventArgs e)
        {
            webBrowser.SetBotMode(false);
            SetBtn(btnStart, true, "Start");
            SetBtn(btnPause, false, "Pause");
        }

        private void OnBotPaused(object o, EventArgs e)
        {
            webBrowser.SetBotMode(false);
            SetBtn(btnStart, true, "Stop");
            SetBtn(btnPause, true, "Resume");
        }

        private void OnPlayerMotivated(object source, MessageEventArgs<OtherPlayer> e)
        {

            List<Button> btns = new List<Button>();
            btns.Add(btnPO);
            btns.Add(btnMO);
            btns.Add(btnMM);

            foreach (Button btn in btns)
                SetBtn(btn,false);
            }


        private void OnPlayerVisited(object source, MessageEventArgs<OtherPlayeCity> e)
        {
            currentPlayerCity = e.Message;
            updatePlayerPanel();

        }

        #endregion


        #region Motivator UI Methods
        private void updatePlayerPanel()
        {
            lblPlayer.InvokeIfRequired(lblP =>
            {
                this.splitContainer1.Panel2Collapsed = false;
                this.lblPlayer.Text = currentPlayerCity.OtherPlayer.Name;
                this.txtPlayerDesciption.Text = currentPlayerCity.OtherPlayer.ProfileText.Replace("\r", Environment.NewLine);
                bool test = currentPlayerCity.OtherPlayer.ProfileText.Contains("\r");
            });


            MotivationOrdersType order = bot.socialBarMotivator.getMotivationOrderFromPlayer(currentPlayerCity.OtherPlayer);
            this.motivationBtns.ToList().ForEach(vp =>
            {
                vp.Value.InvokeIfRequired(btn =>
                {
                    if (bot.socialBarMotivator.isMotivationOrderSet(currentPlayerCity.OtherPlayer) &&
                        bot.socialBarMotivator.getMotivationOrderFromPlayer(currentPlayerCity.OtherPlayer) == vp.Key)
                        btn.Font = new Font(btn.Font, FontStyle.Bold);
                    else
                        btn.Font = new Font(btn.Font, FontStyle.Regular);


                });

            });
        }

        private void OnPlayerNeedMotivationOrder(object sender, MessageEventArgs<OtherPlayeCity> e)
        {
            this.InvokeIfRequired((f) =>
                {
                    _timeoutStart = DateTime.Now;
                    timer.Start();
                    timer.Enabled = true;
                });
        }


        private void btnStart_Click_1(object sender, EventArgs e)
        {
            switch (bot.socialBarMotivator.motivatorState)
            {
                case BotStateEnum.Stopped:
                    webBrowser.ZoomIn();
                    Thread.Sleep(200);
                    bot.socialBarMotivator.Start();
                    break;
                case BotStateEnum.Started:
                case BotStateEnum.Paused:
                    webBrowser.ZoomOut();
                    Thread.Sleep(200);
                    bot.socialBarMotivator.Stop();
                    break;
            }
        }


        private void btnPause_Click(object sender, EventArgs e)
        {
            if (bot.socialBarMotivator.motivatorState == BotStateEnum.Started)
            {
                webBrowser.ZoomOut();
                Thread.Sleep(200); 
                bot.socialBarMotivator.Pause();
            }
            else
            {
                webBrowser.ZoomIn();
                Thread.Sleep(200);
                bot.socialBarMotivator.Start();
            }
        }

        private void btnMotivation_Click(object sender, EventArgs e)
        {
            MotivationOrdersType order = motivationBtns.Single(vp => vp.Value == (Button)sender).Key;
            try
            {
                bot.socialBarMotivator.motivationPreferences.Single(vp => vp.player_id == currentPlayerCity.OtherPlayer.PlayerId).orderPreference
                    = order;
            }
            catch (InvalidOperationException ex)
            {
                MotivationPreference p = new MotivationPreference(currentPlayerCity.OtherPlayer.PlayerId, order);
                bot.socialBarMotivator.motivationPreferences.Add(p);
            }
            bot.socialBarMotivator.motivationPreferences.Save();
            updatePlayerPanel();

            bot.socialBarMotivator.Motivate(currentPlayerCity,
                Motivator.MotivationOrders.Single(mo => mo.Key == order).Value);


            txtInfo.InvokeIfRequired((lbl) => { 
                        timer.Stop(); 
                        lbl.Text = ""; });

        }
        #endregion


        #region General UI Methods


        private void button1_Click(object sender, EventArgs e)
        {
            if (webBrowser.Dock == DockStyle.Fill)
            {
                btnMode.Text = "User Mode";
                webBrowser.SetBotMode(true);
            }
            else
            {
                btnMode.Text = " Bot Mode";
                webBrowser.SetBotMode(false);
            }
        }

        private void btnLog_Click(object sender, EventArgs e)
        {
            logForm.TopMost = true; 
            logForm.Show();
            
        }

        private void btnScreenShot_Click(object sender, EventArgs e)
        {
            webBrowser.TakeScreenshot();
        }

        private void btnSettings_Click(object sender, EventArgs e)
        {
            if (settingsForm == null)
                settingsForm = new SettingsForm();
            settingsForm.ShowDialog();
        }

        private void btnReload_Click(object sender, EventArgs e)
        {
            webBrowser.Reload(false);
        }

        #endregion


        #region IResourceInterceptor Members
        private const string LOGO_RESOURCE = "WinFormsSample.osm_logo_550.png";

        // Note that this is called on the I/O thread.
        ResourceResponse IResourceInterceptor.OnRequest(ResourceRequest request)
        {
/*            // We are only interested in GET requests.
            if (String.Compare(request.Method, "GET", false) != 0)
                return null;

            bool isGoogleHost = request.Url.Host.EndsWith("google.com") ||
                request.Url.Host.EndsWith("ggpht.com") ||
                request.Url.Host.EndsWith("gstatic.com") ||
                request.Url.Host.EndsWith("googleapis.com") ||
                request.Url.Host.EndsWith("googleusercontent.com");

            if (isGoogleHost && (request.Url.Segments.Length > 1))
            {
                // Get the last segment of the Uri. This is usually the file name.
                string fileName = request.Url.Segments[request.Url.Segments.Length - 1];

                //Debug.Print( "Possible file-name: " + fileName );

                // Check if this is a request for 'logo4w.png' (this is currently 
                // the name of the 'Google' logo file).
                if (String.Compare(fileName, "logo4w.png", true) == 0)
                {
                    // Get the embedded resource of the Awesomium logo.
                    var assembly = Assembly.GetExecutingAssembly();
                    var info = assembly.GetManifestResourceInfo(LOGO_RESOURCE);

                    // The resource does not exist.
                    if (info == null)
                        return null;

                    using (var stream = assembly.GetManifestResourceStream(LOGO_RESOURCE))
                    {
                        // Get a byte array of the resource.
                        byte[] buffer = new byte[stream.Length];
                        stream.Read(buffer, 0, buffer.Length);

                        // Initialize unmanaged memory to hold the array.
                        int size = Marshal.SizeOf(buffer[0]) * buffer.Length;
                        IntPtr pnt = Marshal.AllocHGlobal(size);

                        try
                        {
                            // Copy the array to unmanaged memory.
                            Marshal.Copy(buffer, 0, pnt, buffer.Length);

                            // Alternatively, you can pin the array in the managed heap.
                            // Note however that pinning objects seriously disrupts GC operation. 
                            // Being able to move objects around in the heap is one of the reasons 
                            // why modern GCs can (somewhat) keep up with manual memory management. 
                            // By pinning objects in the managed heap, the GC looses it's one 
                            // performance advantage over manual memory management: 
                            // a relatively un-fragmented heap.
                            //GCHandle handle = GCHandle.Alloc( buffer, GCHandleType.Pinned );
                            //IntPtr pnt = handle.AddrOfPinnedObject();

                            // Create a ResourceResponse. A copy is made of the supplied buffer.
                            return ResourceResponse.Create((uint)buffer.Length, pnt, "image/png");
                        }
                        finally
                        {
                            // Data is not owned by the ResourceResponse. A copy is made 
                            // of the supplied buffer. We can safely free the unmanaged memory.
                            Marshal.FreeHGlobal(pnt);
                        }
                    }
                }
            }

*/            // Return NULL to allow normal behavior.
            return null;
        }

        // Note that this is called on the I/O thread.
        bool IResourceInterceptor.OnFilterNavigation(NavigationRequest request)
        {
/*            bool isGoogleHost = request.Url.Host.EndsWith("google.com") ||
                request.Url.Host.EndsWith("ggpht.com") ||
                request.Url.Host.EndsWith("gstatic.com") ||
                request.Url.Host.EndsWith("googleapis.com") ||
                request.Url.Host.EndsWith("googleusercontent.com");

            // Uncomment the following lines, to block (almost) everything
            // outside Google. This will cancel any attempt to navigate away 
            // from Google sites.
            // return !isGoogleHost;
*/
            return false;
        }
        #endregion


        #region Test methods

        public void MotivateWork(MotivateType mType)
        {
            if (mType == MotivateType.PO)
                bot.ClickPolish();
            else
                bot.ClickMotivate();
            Thread.Sleep(200);
            bot.Click(batiments[mType]);
        }

        private void btnPO_Click(object sender, EventArgs e)
        {
            new Thread(() => MotivateWork(MotivateType.PO)).Start();
        }

        private void btnMO_Click(object sender, EventArgs e)
        {
            new Thread(() => MotivateWork(MotivateType.MO)).Start();
        }

        private void btnMM_Click(object sender, EventArgs e)
        {
            new Thread(() => MotivateWork(MotivateType.MM)).Start();
        }

        private void btnPolish_Click(object sender, EventArgs e)
        {
            bot.ClickPolish();
        }

        private void btnClickBuilding_Click(object sender, EventArgs e)
        {
            bot.ClickMap(int.Parse(txtX.Text), int.Parse(txtY.Text));
        }

        private void mouseMouse_Click(object sender, EventArgs e)
        {

            webBrowser.LeftClick(int.Parse(txtX.Text), int.Parse(txtY.Text));

        }

        private void btnClickCoords_Click(object sender, EventArgs e)
        {
            webBrowser.LeftClick(int.Parse(txtX.Text), int.Parse(txtY.Text));
        }


        private void btnPrevious_Click(object sender, EventArgs e)
        {
            bot.socialBarMotivator.ClickPrevious();
        }

        private void btnNext_Click(object sender, EventArgs e)
        {
            bot.socialBarMotivator.ClickNext();
        }

        private void btnVisitNext_Click(object sender, EventArgs e)
        {

            txtInfo.InvokeIfRequired((lbl) => { timer.Stop();
                                                lbl.Text = ""; });
            bot.socialBarMotivator.VisitNext(500);
        }

        private void button7_Click(object sender, EventArgs e)
        {
            //            Log("isZoomedIn {0} isZoomedOut {1}", webBrowser.FoeIsZoomedIn(), webBrowser.FoeIsZoomedOut());
            webBrowser.FoeZoomIn();
        }

        #endregion



        private void timer_Tick(object sender, EventArgs e)
        {
            string text;
            DateTime end = _timeoutStart.AddSeconds(Bot.settings.waitForUserSelectMotivationDelay);
            if (DateTime.Now > end)
            {
                if (Bot.settings.useDefaultIfNotSet)
                    bot.socialBarMotivator.Motivate(currentPlayerCity, Motivator.GetListFromMotivationOrderType(Bot.settings.defaultMotivationOrder));
                else
                    bot.socialBarMotivator.VisitNext(1000);
                txtInfo.InvokeIfRequired((lbl) =>
                {
                    timer.Stop();
                    lbl.Text = "";
                });
            }
            else
            {
                TimeSpan span = end - DateTime.Now;

                if (Bot.settings.useDefaultIfNotSet)
                    text = "Motivating current player with default ";
                else
                    text = "Switching to next player ";
                text += "in " + ((int)(span.TotalSeconds)).ToString() + " s";
                txtInfo.InvokeIfRequired((lbl) => { lbl.Text = text; });
            }
        }

        private void btnDefault_Click(object sender, EventArgs e)
        {

            txtInfo.InvokeIfRequired((lbl) => { 
                            timer.Stop(); 
                            lbl.Text = ""; });
            bot.socialBarMotivator.Motivate(currentPlayerCity, 
                Motivator.GetListFromMotivationOrderType(Bot.settings.defaultMotivationOrder));
        }




    }

}
